package com.evil.application.util;

import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSON;
import com.evil.application.convert.DataConvert;
import com.evil.application.enums.form.control.ControlCodeEnum;
import com.evil.application.enums.order.OrderSpecialControlEnum;
import com.evil.application.mapper.ApplicationFormControlMapper;
import com.evil.application.mapper.ApplicationFormMapper;
import com.evil.application.pojo.dto.form.control.ControlBaseInfoDTO;
import com.evil.application.pojo.dto.form.control.code.ControlDTO;
import com.evil.application.pojo.dto.form.control.code.OrderSelectControlDTO;
import com.evil.application.pojo.dto.form.control.code.config.FilterConfigDTO;
import com.evil.application.pojo.dto.order.RelatedDataReqDTO;
import com.evil.application.pojo.entity.ApplicationForm;
import com.evil.application.service.ElasticsearchService;
import com.evil.common.account.util.LoginUtil;
import com.evil.common.application.dto.form.SpecialControlConfigDTO;
import com.evil.common.application.dto.order.data.ControlDataDTO;
import com.evil.common.application.dto.order.data.ControlDataValueDTO;
import com.evil.common.core.entity.QueryModel;
import com.evil.common.core.enums.SwitchEnum;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 重新触发 关联控件 与 公式
 */
@Slf4j
@Component
@AllArgsConstructor
public class ReTriggerUtil {

    /**
     * 公式里的控件id前缀
     */
    public final static String FORMULA_CONTROL_PREFIX = "#ctrlId_";
    /**
     * 公式里的控件id
     */
    public final static Pattern FORMULA_CONTROL_PATTERN = Pattern.compile(FORMULA_CONTROL_PREFIX + "(-?\\d+)\\$");
    public final static Pattern FORMULA_CONTROL_ID_PATTERN = Pattern.compile("-?\\d+");

    private final LoginUtil loginUtil;

    private final MatchingUtil matchingUtil;

    private final ApplicationFormMapper applicationFormMapper;

    private final ApplicationFormControlMapper applicationFormControlMapper;

    private final ElasticsearchService elasticsearchService;

    /**
     * 获取公式中所有使用的控件
     *
     * @param formula formula
     * @return List
     */
    public static List<Long> getFormulaControlIds(String formula) {
        return ReTriggerUtil.getFormulaFieldKey(formula).stream().map(ReTriggerUtil::getControlIdByFieldKey).collect(Collectors.toList());
    }

    /**
     * 替换公式，有更改则有返回值
     *
     * @param formula      formula
     * @param controlIdMap controlIdMap
     * @return Optional
     */
    public static Optional<String> replaceFormulaControlId(String formula, Map<Long, Long> controlIdMap) {
        boolean change = false;
        for (String s : ReTriggerUtil.getFormulaFieldKey(formula)) {
            // 如果存在
            if (controlIdMap.containsKey(ReTriggerUtil.getControlIdByFieldKey(s))) {
                // 替换
                formula = formula.replaceAll(ReTriggerUtil.getControlIdByFieldKey(s).toString(), controlIdMap.get(ReTriggerUtil.getControlIdByFieldKey(s)).toString());
                change = true;
            }
        }
        return change ? Optional.of(formula) : Optional.empty();
    }

    /**
     * 获取公式中所有 单元
     *
     * @param formula formula
     * @return List
     */
    public static List<String> getFormulaFieldKey(String formula) {
        List<String> formulaList = new ArrayList<>();
        // 如果公式不为空并且公式里包含字段
        if (StringUtils.isNotBlank(formula)) {
            Matcher m = ReTriggerUtil.FORMULA_CONTROL_PATTERN.matcher(formula);
            while (m.find()) {
                formulaList.add(m.group(0));
            }
        }
        return formulaList;
    }

    /**
     * 获取公式单元中 控件id
     *
     * @param fieldKey fieldKey
     * @return Long
     */
    public static Long getControlIdByFieldKey(String fieldKey) {
        Matcher m = ReTriggerUtil.FORMULA_CONTROL_ID_PATTERN.matcher(fieldKey);
        long value = 0;
        while (m.find()) {
            if (StringUtils.isNotBlank(m.group(0))) {
                value = Long.parseLong(m.group(0));
            }
        }
        return value;
    }

    public static String generateFieldKey(Long controlId) {
        return String.format("%s%s$", FORMULA_CONTROL_PREFIX, controlId);
    }

    /**
     * 重新触发关联控件与公式
     *
     * @param controlMap              控件Map
     * @param controlDataMap          控件值Map
     * @param recalculationControlIds 需要重新计算公式列表
     */
    public void reTrigger(Map<Long, ControlDTO> controlMap, Map<Long, ControlDataDTO> controlDataMap, List<Long> recalculationControlIds) {
        DataConvert dataConvert = new DataConvert(loginUtil).initFormMap().initControlMap();
        // 遍历需要重新计算公式的控件ids
        for (Long controlId : recalculationControlIds) {
            // 存在的控件才重新运算公式
            if (!controlMap.containsKey(controlId)) {
                log.error("【重新触发关联控件与公式】，需要重新运算公式控件不存在，控件id:{}", controlId);
                continue;
            }
            ControlDTO controlDTO = controlMap.get(controlId);

            if (!controlDataMap.containsKey(controlId)) {
                log.info("【重新触发关联控件与公式】，需要重新运算公式控件不存在，需要补充，控件id:{}", controlId);
                controlDataMap.put(controlId, ApplicationUtil.controlToControlData(controlDTO));
            }

            // 重新触发关联控件
            if (ApplicationUtil.checkControlRelated(controlDTO)) {
                this.related(controlDTO, controlDataMap, dataConvert);
            }
            // 重新触发公式
            else {
                this.formula(controlDTO, controlMap, controlDataMap);
            }
        }
    }

    /**
     * 重新关联数据
     *
     * @param controlDTO     controlDTO
     * @param controlDataMap controlDataMap
     * @param dataConvert    dataConvert
     */
    public void related(ControlDTO controlDTO, Map<Long, ControlDataDTO> controlDataMap, DataConvert dataConvert) {
        log.info("【关联数据】，需要重新关联数据控件值，控件id:{}", controlDTO.getControlId());

        ApplicationForm relatedForm = dataConvert.getForm(controlDTO.getRelatedFormId());
        ControlDTO relatedControl = OrderSpecialControlEnum.isExistById(controlDTO.getRelatedControlId())
                .map(em -> {
                    SpecialControlConfigDTO specialControlConfigDTO = JSON.parseObject(relatedForm.getSpecialControlConfig(), SpecialControlConfigDTO.class);
                    return ApplicationUtil.createSpecialControlDTO(em, specialControlConfigDTO);
                })
                .orElseGet(() -> dataConvert.getControlDTO(controlDTO.getRelatedControlId()));
        FilterConfigDTO relateFilterConfig = controlDTO.getRelateFilterConfig();

        RelatedDataReqDTO relatedDataReqDTO = new RelatedDataReqDTO();
        relatedDataReqDTO.setApplicationId(relatedForm.getApplicationId());
        relatedDataReqDTO.setControl(relatedControl);
        relatedDataReqDTO.setBeingTableControl(ControlCodeEnum.TABLE.getCode().equals(relatedControl.getControlCode()));

        // 处理筛选条件
        OrderDataUtil.handleMatchingCondition(relateFilterConfig.getJudgeOrs(), controlDataMap, matchingUtil::dynamic);
        relatedDataReqDTO.setJudgeOrs(relateFilterConfig.getJudgeOrs());

        // 数据关联控件 && 开启汇总
        if (controlDTO instanceof OrderSelectControlDTO) {
            OrderSelectControlDTO orderSelectControlDTO = (OrderSelectControlDTO) controlDTO;
            if (SwitchEnum.isOpen(orderSelectControlDTO.getSummaryStatus())) {
                // 汇总方式
                relatedDataReqDTO.setSummaryType(orderSelectControlDTO.getSummaryType());
            }
        }
        // 获取关联数据
        List<ControlDataDTO> controlDataList = elasticsearchService.relatedDataList(new QueryModel<>(relatedDataReqDTO));
        controlDataList.stream().findFirst().ifPresent(c -> {
            ControlDataDTO controlDataDTO = controlDataMap.get(controlDTO.getControlId());
            // 复制值
            OrderDataUtil.copyControlData(c, controlDataDTO, new ControlBaseInfoDTO(controlDTO).getOptions());
        });
    }

    /**
     * 重新触发公式
     *
     * @param controlDTO     controlDTO
     * @param controlMap     controlMap
     * @param controlDataMap controlDataMap
     */
    public void formula(ControlDTO controlDTO, Map<Long, ControlDTO> controlMap, Map<Long, ControlDataDTO> controlDataMap) {
    }

    /**
     * 计算控件中的公式
     *
     * @param controlDTO     控件
     * @param controlMap     控件Map
     * @param controlDataMap 控件数据Map
     * @return List 计算结果值
     */
    public List<ControlDataValueDTO> formula(ControlDTO controlDTO, ControlDTO parentControlDTO, Map<Long, ControlDTO> controlMap, Map<Long, ControlDataDTO> controlDataMap) {
        return null;
    }

    /**
     * 处理公式中使用其他控件值
     *
     * @param formula            公式
     * @param formulaControlData 其他控件值
     * @return String
     */
    private String handlerFormula(String formula, ControlDataDTO formulaControlData) {
        ControlCodeEnum controlCodeEnum = ControlCodeEnum.findByCode(formulaControlData.getControlCode());

        // 其他控件 替换控件值
        String value;
        List<String> values = this.formatValue(OrderDataUtil.controlDataValues(formulaControlData));
        if (controlCodeEnum.isMultiple()) {
            // 公式使用的字段是多值控件
            value = JSON.toJSONString(values);
        } else {
            // 公式使用的字段是单值控件
            value = String.join(",", values);
        }
        String key = ReTriggerUtil.generateFieldKey(formulaControlData.getControlId());
        while (formula.contains(key)) {
            formula = formula.replace(key, value);
        }
        return formula;
    }

    private List<String> formatValue(List<String> values) {
        try {
            // 全部都是数字类型的值
            return values.stream().map(NumberUtil::toBigDecimal).map(e -> e.stripTrailingZeros().toPlainString()).collect(Collectors.toList());
        } catch (Exception ignored) {
            // 否则 字段串类型
            return values.stream().map(e -> String.format("\"%s\"", e)).collect(Collectors.toList());
        }
    }
}
